 /***********************************************************
 *  File_Name  : XJTxtLog.cpp
 *  File_Path   : 
 *  File_Encode : UTF8
 *  Description : 文本日志类
 *  
 *  OS : Linux, UNIX
 *  LastVersion  : 20130723
 *      
 *  Author      : LiXiujie(李秀杰)
 *  Date        : 2014-4-17
 *  Version     : 20130417
 *  Description : 
 *                
 *  Edit Author : 
 *  Edit Date   : 
 *  Version     : 
 *  Description :
 *                
 ***********************************************************/
#include "XJTxtLog.h"
#include <sys/syscall.h>

namespace XJUtilClass{

XJTxtLog::XJTxtLog(){
	pthread_mutex_init(&m_MutexFile, NULL);
	pthread_mutex_init(&m_MutexStrCache, NULL);
	memset(m_szFilePath, 0x00, sizeof(m_szFilePath));
	memset(m_szFileName, 0x00, sizeof(m_szFileName));
	m_iLevel = XJTXTLOG_LEVEL_DEBUG;
	strcpy(m_szFilePath, "./");
	strcpy(m_szFileName, "XJTxt.log");
	m_lSize = XJTXTLOG_CACHESIZE;
	m_iMode = XJTXTLOG_MODE_NOCACHE;
	m_iFile = -1;
	m_IsThreadRun = false;
	m_nFlushTimeout = 1*1000*1000;
	m_nThreadRunCount = 0;
	if (XJTXTLOG_MODE_CACHE == m_iMode){
		m_IsThreadRun = true;
		CreateThread();
	}
}
XJTxtLog::XJTxtLog(XJTxtLogLevel iLevel, const char *szFilePath, const char* szFileName,
		XJTxtLogMode iMode, unsigned long ulCacheSize){
	pthread_mutex_init(&m_MutexFile, NULL);
	pthread_mutex_init(&m_MutexStrCache, NULL);
	memset(m_szFilePath, 0x00, sizeof(m_szFilePath));
	memset(m_szFileName, 0x00, sizeof(m_szFileName));
	m_iLevel = iLevel;
	strcpy(m_szFilePath, szFilePath);
	if (strlen(m_szFilePath) > 0 ){
		if (m_szFilePath[strlen(m_szFilePath) - 1] != '/' &&
			m_szFilePath[strlen(m_szFilePath) - 1] != '\\')
			m_szFilePath[strlen(m_szFilePath)] = '/';
	}
	strncpy(m_szFileName, szFileName, sizeof(m_szFileName));
	m_lSize = ulCacheSize;
	m_iMode = iMode;
	m_IsThreadRun = false;
	m_iFile = -1;
	m_nFlushTimeout = 1*1000*1000;
	m_nThreadRunCount = 0;
	if (XJTXTLOG_MODE_CACHE == m_iMode){
		m_IsThreadRun = true;
		CreateThread();
	}
}

XJTxtLog::~XJTxtLog(){
	m_IsThreadRun = false;
	while(m_nThreadRunCount){
		::usleep(1000);
	}
	if (-1 != m_iFile){
		close(m_iFile);
		m_iFile = -1;
	}
	pthread_mutex_destroy(&this->m_MutexFile);
	pthread_mutex_destroy(&this->m_MutexStrCache);
}

void XJTxtLog::getDataAndTime(char* pDateBuf,char* pTimeBuf)
{
	struct timeval tms;
	timerclear(&tms);
	gettimeofday(&tms,NULL);
    struct tm *pTime = localtime(&tms.tv_sec);
    sprintf(pDateBuf,"%04d%02d%02d",pTime->tm_year+1900,
               pTime->tm_mon+1,
               pTime->tm_mday);
    sprintf(pTimeBuf,"%02d:%02d:%02d %03d",pTime->tm_hour,
            pTime->tm_min,
            pTime->tm_sec,
            tms.tv_usec/1000);
}

void XJTxtLog::GetLevelMsg(char* pMsg,XJTxtLogLevel iLevel){
	switch(iLevel)
	{
	case XJTXTLOG_LEVEL_DEBUG:
		strcpy(pMsg,"DEBUG|");
		break;
	case XJTXTLOG_LEVEL_ERROR:
		strcpy(pMsg,"ERROR|");
		break;
	case XJTXTLOG_LEVEL_INFO:
		strcpy(pMsg," INFO|");
		break;
	case XJTXTLOG_LEVEL_FATAL:
		strcpy(pMsg,"FATAL|");
		break;
	case XJTXTLOG_LEVEL_WARN:
		strcpy(pMsg," WARN|");
		break;
	}
	return;
}

int XJTxtLog::writeLog(XJTxtLogLevel iLevel, const char* szText){
	int ret = 0;
	char szMsg[200]={0x00};
	char szDate[9]={0x00};
	char szTime[15]={0x00};
	char szLevel[10]={0x00};
	this->GetLevelMsg(szLevel, iLevel);
	this->getDataAndTime(szDate,szTime);
	unsigned long nPid = getpid();
	unsigned long nTid = syscall(SYS_gettid);
	sprintf(szMsg,"[%s][P:%lu][T:%lu]%s",szTime, nPid, nTid, szLevel);
	pthread_mutex_lock(&m_MutexStrCache);
	m_strCache = m_strCache + szMsg +  szText + "\n";
	if (XJTXTLOG_MODE_NOCACHE == m_iMode || 0 == m_lSize){
		//pthread_mutex_lock(&m_MutexFile);
		ret = writeFile();
		//pthread_mutex_unlock(&m_MutexFile);
		if (ret == 0){
			m_strCache.clear();
		}else {
			if (m_lSize < XJTXTLOG_CACHESIZE && m_strCache.size() > XJTXTLOG_CACHESIZE*32)
				m_strCache.clear();
			else if (m_lSize >= XJTXTLOG_CACHESIZE && m_strCache.size() > m_lSize*16)
				m_strCache.clear();
		}
	}else{
		if (m_strCache.size() > m_lSize){
			//pthread_mutex_lock(&m_MutexFile);
			ret = writeFile();
			//pthread_mutex_unlock(&m_MutexFile);
			if (ret == 0){
				m_strCache.clear();
			}else {
				if (m_lSize < XJTXTLOG_CACHESIZE && m_strCache.size() > XJTXTLOG_CACHESIZE*32)
					m_strCache.clear();
				else if (m_lSize >= XJTXTLOG_CACHESIZE && m_strCache.size() > m_lSize*16)
					m_strCache.clear();
			}
		}
	}
	pthread_mutex_unlock(&m_MutexStrCache);
	return ret;
}

int XJTxtLog::writeFile(){
	char cdate[9]={0x00};
	char ctime[9]={0x00};
	this->getDataAndTime(cdate,ctime);
	char szFileAllPath[512] = { 0x00 };
	sprintf(szFileAllPath, "%s%s-%s", m_szFilePath, cdate, m_szFileName);
	if (strcmp(szFileAllPath, m_szFileAllPath) == 0){
		if (-1 == m_iFile){
			m_iFile = open(szFileAllPath, O_RDWR);
			if (-1 == m_iFile)
				m_iFile = open(szFileAllPath, O_WRONLY|O_CREAT|O_TRUNC, 0666);
		}
	}else{
		if (-1 != m_iFile){
			close(m_iFile);
			m_iFile = -1;
		}
		if (-1 == m_iFile){
			m_iFile = open(szFileAllPath, O_RDWR);
			if (-1 == m_iFile)
				m_iFile = open(szFileAllPath, O_WRONLY|O_CREAT|O_TRUNC, 0666);
		}
		memset(m_szFileAllPath, 0x00, sizeof(m_szFileAllPath));
		strcpy(m_szFileAllPath, szFileAllPath);
	}
	if (-1 != m_iFile && lseek(m_iFile, 0, SEEK_END)< -1){
		close(m_iFile);
		m_iFile = -1;
	}
	if (-1 == m_iFile){
		printf("Error:XJTxtLog::writeFile 打开文件失败[%s] \n", m_szFileAllPath);
		return XJTXTLOG_ERROR_FILE_OPEN;
	}
	unsigned long ulLen = 0;
	ulLen = write(m_iFile, m_strCache.c_str(), m_strCache.size());
	if (ulLen != m_strCache.size()){
		printf("Error:XJTxtLog::CreateThread 创建线程失败Ret[%lu]errno[%d]errstr[%s]",
				ulLen, errno, strerror(errno));
		return XJTXTLOG_ERROR_WRITE;
	}
	return XJTXTLOG_OK;
}
int XJTxtLog::CreateThread(){
	int ret = 0;
	pthread_t nThread;
	pthread_attr_t attrThread;
	pthread_attr_init(&attrThread);
	pthread_attr_setdetachstate(&attrThread,  PTHREAD_CREATE_DETACHED);
	ret = pthread_create(&nThread, &attrThread,
			(XJTXTLOG_THREADFUN)XJTxtLog::processThread, (void*)this);
	if (ret != 0)
	{
		printf("Error:XJTxtLog::CreateThread 创建线程失败Ret[%d]errno[%d]errstr[%s]",
				ret, errno, strerror(errno));
		return XJTXTLOG_ERROR_OTHER;
	}
	else
	{
		return XJTXTLOG_OK;
	}
}
void* XJTxtLog::processThread(XJTxtLog* p){
	int ret = 0;
	unsigned int nFailCount = 0;
	while (p->m_IsThreadRun ||
			(p->m_strCache.size() > 0 && p->m_iMode == XJTXTLOG_MODE_CACHE)){
		pthread_mutex_lock(&p->m_MutexStrCache);
		if (p->m_strCache.size() > 0){
			//pthread_mutex_lock(&p->m_MutexFile);
			ret = p->writeFile();
			//pthread_mutex_unlock(&p->m_MutexFile);
			if (ret == 0){
				p->m_strCache.clear();
			}else {
				if (p->m_lSize < XJTXTLOG_CACHESIZE && p->m_strCache.size() > XJTXTLOG_CACHESIZE*32)
					p->m_strCache.clear();
				else if (p->m_lSize >= XJTXTLOG_CACHESIZE && p->m_strCache.size() > p->m_lSize*16)
					p->m_strCache.clear();
			}
		}
		pthread_mutex_unlock(&p->m_MutexStrCache);
		if (p->m_nFlushTimeout < 10){
			p->m_nFlushTimeout = 10;
		}
		if (p->m_nFlushTimeout%1000000){
			::usleep(p->m_nFlushTimeout%1000000);
		}
		if (p->m_nFlushTimeout/1000000){
			::sleep(p->m_nFlushTimeout/1000000);
		}
	}
	return (void*)0;
}

int XJTxtLog::Debug(XJTxtLog* pLog, const char* szFormat, ...){
	if (0 == pLog){
		return XJTXTLOG_ERROR_NULL;
	}
	if (XJTXTLOG_LEVEL_DEBUG < pLog->m_iLevel)
		return XJTXTLOG_ERROR_LEVEL;
	unsigned int nBufSize = XJTXTLOG_CACHEMAXSIZE;
	if (nBufSize < pLog->m_lSize + 2048){
		nBufSize = pLog->m_lSize + 2048;
	}
	char *pBuf = new char[nBufSize];
	if (NULL == pBuf){
		return XJTXTLOG_ERROR_MALLOC;
	}
	memset(pBuf, 0, nBufSize);
	va_list args;
	va_start(args, szFormat);
	vsnprintf(pBuf, nBufSize-1, szFormat, args);
	va_end(args);
	int ret = pLog->writeLog(XJTXTLOG_LEVEL_DEBUG, pBuf);
	delete pBuf;
	return ret;
}

int XJTxtLog::Info(XJTxtLog* pLog, const char* szFormat, ...){
	if (0 == pLog){
		return XJTXTLOG_ERROR_NULL;
	}
	if (XJTXTLOG_LEVEL_INFO < pLog->m_iLevel)
		return XJTXTLOG_ERROR_LEVEL;
	unsigned int nBufSize = XJTXTLOG_CACHEMAXSIZE;
	if (nBufSize < pLog->m_lSize + 2048){
		nBufSize = pLog->m_lSize + 2048;
	}
	char *pBuf = new char[nBufSize];
	if (NULL == pBuf){
		return XJTXTLOG_ERROR_MALLOC;
	}
	memset(pBuf, 0, nBufSize);
	va_list args;
	va_start(args, szFormat);
	vsnprintf(pBuf, nBufSize-1, szFormat, args);
	va_end(args);
	int ret = pLog->writeLog(XJTXTLOG_LEVEL_INFO, pBuf);
	delete pBuf;
	return ret;
}

int XJTxtLog::Warn(XJTxtLog* pLog, const char* szFormat, ...){
	if (0 == pLog){
		return XJTXTLOG_ERROR_NULL;
	}
	if (XJTXTLOG_LEVEL_WARN < pLog->m_iLevel)
		return XJTXTLOG_ERROR_LEVEL;
	unsigned int nBufSize = XJTXTLOG_CACHEMAXSIZE;
	if (nBufSize < pLog->m_lSize + 2048){
		nBufSize = pLog->m_lSize + 2048;
	}
	char *pBuf = new char[nBufSize];
	if (NULL == pBuf){
		return XJTXTLOG_ERROR_MALLOC;
	}
	memset(pBuf, 0, nBufSize);
	va_list args;
	va_start(args, szFormat);
	vsnprintf(pBuf, nBufSize-1, szFormat, args);
	va_end(args);
	int ret = pLog->writeLog(XJTXTLOG_LEVEL_WARN, pBuf);
	delete pBuf;
	return ret;
}

int XJTxtLog::Error(XJTxtLog* pLog, const char* szFormat, ...){
	if (0 == pLog){
		return XJTXTLOG_ERROR_NULL;
	}
	if (XJTXTLOG_LEVEL_ERROR < pLog->m_iLevel)
		return XJTXTLOG_ERROR_LEVEL;
	unsigned int nBufSize = XJTXTLOG_CACHEMAXSIZE;
	if (nBufSize < pLog->m_lSize + 2048){
		nBufSize = pLog->m_lSize + 2048;
	}
	char *pBuf = new char[nBufSize];
	if (NULL == pBuf){
		return XJTXTLOG_ERROR_MALLOC;
	}
	memset(pBuf, 0, nBufSize);
	va_list args;
	va_start(args, szFormat);
	vsnprintf(pBuf, nBufSize-1, szFormat, args);
	va_end(args);
	int ret = pLog->writeLog(XJTXTLOG_LEVEL_ERROR, pBuf);
	delete pBuf;
	return ret;
}

int XJTxtLog::Fatal(XJTxtLog* pLog, const char* szFormat, ...){
	if (0 == pLog){
		return XJTXTLOG_ERROR_NULL;
	}
	if (XJTXTLOG_LEVEL_FATAL < pLog->m_iLevel)
		return XJTXTLOG_ERROR_LEVEL;
	unsigned int nBufSize = XJTXTLOG_CACHEMAXSIZE;
	if (nBufSize < pLog->m_lSize + 2048){
		nBufSize = pLog->m_lSize + 2048;
	}
	char *pBuf = new char[nBufSize];
	if (NULL == pBuf){
		return XJTXTLOG_ERROR_MALLOC;
	}
	memset(pBuf, 0, nBufSize);
	va_list args;
	va_start(args, szFormat);
	vsnprintf(pBuf, nBufSize-1, szFormat, args);
	va_end(args);
	int ret = pLog->writeLog(XJTXTLOG_LEVEL_FATAL, pBuf);
	delete pBuf;
	return ret;
}

int XJTxtLog::debug(const char* szFormat, ...){
	if (XJTXTLOG_LEVEL_DEBUG < m_iLevel)
		return XJTXTLOG_ERROR_LEVEL;
	unsigned int nBufSize = XJTXTLOG_CACHEMAXSIZE;
	if (nBufSize < m_lSize + 2048){
		nBufSize = m_lSize + 2048;
	}
	char *pBuf = new char[nBufSize];
	if (NULL == pBuf){
		return XJTXTLOG_ERROR_MALLOC;
	}
	memset(pBuf, 0, nBufSize);
	va_list args;
	va_start(args, szFormat);
	vsnprintf(pBuf, nBufSize-1, szFormat, args);
	va_end(args);
	int ret = writeLog(XJTXTLOG_LEVEL_DEBUG, pBuf);
	delete pBuf;
	return ret;
}

int XJTxtLog::info(const char* szFormat, ...){
	if (XJTXTLOG_LEVEL_INFO < m_iLevel)
		return XJTXTLOG_ERROR_LEVEL;
	unsigned int nBufSize = XJTXTLOG_CACHEMAXSIZE;
	if (nBufSize < m_lSize + 2048){
		nBufSize = m_lSize + 2048;
	}
	char *pBuf = new char[nBufSize];
	if (NULL == pBuf){
		return XJTXTLOG_ERROR_MALLOC;
	}
	memset(pBuf, 0, nBufSize);
	va_list args;
	va_start(args, szFormat);
	vsnprintf(pBuf, nBufSize-1, szFormat, args);
	va_end(args);
	int ret = writeLog(XJTXTLOG_LEVEL_INFO, pBuf);
	delete pBuf;
	return ret;
}

int XJTxtLog::warn(const char* szFormat, ...){
	if (XJTXTLOG_LEVEL_WARN < m_iLevel)
		return XJTXTLOG_ERROR_LEVEL;
	unsigned int nBufSize = XJTXTLOG_CACHEMAXSIZE;
	if (nBufSize < m_lSize + 2048){
		nBufSize = m_lSize + 2048;
	}
	char *pBuf = new char[nBufSize];
	if (NULL == pBuf){
		return XJTXTLOG_ERROR_MALLOC;
	}
	memset(pBuf, 0, nBufSize);
	va_list args;
	va_start(args, szFormat);
	vsnprintf(pBuf, nBufSize-1, szFormat, args);
	va_end(args);
	int ret = writeLog(XJTXTLOG_LEVEL_WARN, pBuf);
	delete pBuf;
	pBuf = NULL;
	return ret;
}

int XJTxtLog::error(const char* szFormat, ...){
	if (XJTXTLOG_LEVEL_ERROR < m_iLevel)
		return XJTXTLOG_ERROR_LEVEL;
	unsigned int nBufSize = XJTXTLOG_CACHEMAXSIZE;
	if (nBufSize < m_lSize + 2048){
		nBufSize = m_lSize + 2048;
	}
	char *pBuf = new char[nBufSize];
	if (NULL == pBuf){
		return XJTXTLOG_ERROR_MALLOC;
	}
	memset(pBuf, 0, nBufSize);
	va_list args;
	va_start(args, szFormat);
	vsnprintf(pBuf, nBufSize-1, szFormat, args);
	va_end(args);
	int ret = writeLog(XJTXTLOG_LEVEL_ERROR, pBuf);
	delete pBuf;
	pBuf = NULL;
	return ret;
}

int XJTxtLog::fatal(const char* szFormat, ...){
	if (XJTXTLOG_LEVEL_FATAL < m_iLevel)
		return XJTXTLOG_ERROR_LEVEL;
	unsigned int nBufSize = XJTXTLOG_CACHEMAXSIZE;
	if (nBufSize < m_lSize + 2048){
		nBufSize = m_lSize + 2048;
	}
	char *pBuf = new char[nBufSize];
	if (NULL == pBuf){
		return XJTXTLOG_ERROR_MALLOC;
	}
	memset(pBuf, 0, nBufSize);
	va_list args;
	va_start(args, szFormat);
	vsnprintf(pBuf, nBufSize-1, szFormat, args);
	va_end(args);
	int ret = writeLog(XJTXTLOG_LEVEL_FATAL, pBuf);
	delete pBuf;
	pBuf = NULL;
	return ret;
}


XJTxtLogLevel XJTxtLog::getLevelTxt(const char *szLevelTxt){
	if (szLevelTxt == NULL){
		return XJTXTLOG_LEVEL_INFO;
	}
	char chLevel = toupper(szLevelTxt[0]);
	if ('D' == chLevel)
		return XJTXTLOG_LEVEL_DEBUG;
	else if ('I' == chLevel)
		return XJTXTLOG_LEVEL_INFO;
	else if ('W' == chLevel)
		return XJTXTLOG_LEVEL_WARN;
	else if ('E' == chLevel)
		return XJTXTLOG_LEVEL_ERROR;
	else if ('F' == chLevel)
		return XJTXTLOG_LEVEL_FATAL;
	else
		return XJTXTLOG_LEVEL_INFO;
	return XJTXTLOG_LEVEL_INFO;
}

void XJTxtLog::setLevel(XJTxtLogLevel level){
	m_iLevel = level;
}

int XJTxtLog::getLevel(){
	return m_iLevel;
}

void XJTxtLog::setMode(XJTxtLogMode iMode){
	m_iMode = iMode;
	if (XJTXTLOG_MODE_CACHE == m_iMode){
		m_IsThreadRun = true;
		CreateThread();
	}
}

int XJTxtLog::getMode(){
	return m_iMode;
}

void XJTxtLog::setLogFilePath(const char* szFilePath){
	memset(m_szFilePath, 0x00, sizeof(m_szFilePath));
	if (szFilePath != NULL)
		strncpy(m_szFilePath, szFilePath, sizeof(m_szFilePath));
	else
		strcpy(m_szFilePath, "./");
	if (strlen(m_szFilePath) > 0 ){
		if (m_szFilePath[strlen(m_szFilePath) - 1] != '/' &&
			m_szFilePath[strlen(m_szFilePath) - 1] != '\\')
			m_szFilePath[strlen(m_szFilePath)] = '/';
	}
}

const char* XJTxtLog::getLogFilePath(){
	return (const char*)m_szFilePath;
}

void XJTxtLog::setLogFileName(const char* szFileName){
	memset(m_szFileName, 0x00, sizeof(m_szFileName));
	if (szFileName != NULL)
		strncpy(m_szFileName, szFileName, sizeof(m_szFileName));
	else
		strcpy(m_szFileName, "XJTxt.log");
}

const char* XJTxtLog::getLogFileName(){
	return (const char*)m_szFileName;
}

void XJTxtLog::setFlushTimeout(unsigned int nMicro){
	m_nFlushTimeout = nMicro;
}

unsigned int XJTxtLog::getFlushTimeout(){
	return m_nFlushTimeout;
}
void XJTxtLog::setCacheSize(unsigned long ulCacheSize){
	m_lSize = ulCacheSize;
}
unsigned long XJTxtLog::getCacheSize(){
	return m_lSize;
}
}//namespace XJUtilClass{
